package ch.sv7.tool.dbmanager.db.fix;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import ch.sv7.tool.dbmanager.StringUtil;
import ch.sv7.tool.dbmanager.db.ColumnSchema;
import ch.sv7.tool.dbmanager.db.SchemaElement;
import ch.sv7.tool.dbmanager.db.dialect.CustomTypeConversion;
import ch.sv7.tool.dbmanager.db.dialect.DialectException;
import ch.sv7.tool.log.Logger;

/**
 * TODO: Document me!
 *
 * @author svt
 *
 */
public class CopyCustomConvertColumnFix extends AbstractSchemaFix implements SchemaFix {

	private static final Logger log = Logger.getLogger(CopyCustomConvertColumnFix.class);
	
	private ColumnSchema fromColumn;
	private ColumnSchema toColumn;
	private CustomTypeConversion typeConversion;

	/**
	 * @param fromColumn
	 * @param toColumn
	 * @param typeConversion
	 */
	public CopyCustomConvertColumnFix(ColumnSchema fromColumn, ColumnSchema toColumn,
			CustomTypeConversion typeConversion) {
		super();
		this.fromColumn = fromColumn;
		this.toColumn = toColumn;
		this.typeConversion = typeConversion;
	}

	public SchemaElement getActionTarget() {
		return toColumn;
	}
	
	public String getSQLFix() throws DialectException {
		String ddl = "\n-- Data copy with custom conversion " 
			+ typeConversion.getClass().getName() 
			+ " from " 
			+ fromColumn.getTable().getName() + "." + fromColumn.getName()
			+ " to " 
			+ toColumn.getName() 
			+ "\n";
		return ddl;
	}

	public void executeFixing(Connection conn) throws DialectException, SQLException {
		Statement stmt = null;
		PreparedStatement updateStatement = null;
		ResultSet rs = null;
		long count = 0;
		List columnNames = new ArrayList();
		columnNames.add(fromColumn.getName());
		columnNames.add(toColumn.getName());
		ColumnSchema[] pks = fromColumn.getTable().getPrimaryKeyColumnSchemas();
		for (int i=0; i<pks.length; i++) {
			columnNames.add(pks[i].getName());
		}
		try {
			log.info("Converting data from "			
					+ fromColumn.getTable().getName() + "." + fromColumn.getName()
					+ " to " 
					+ toColumn.getName() 
					+ " conversion: " + typeConversion.getClass().getName());
			stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
			
			
			String sql = "select " 
				+ StringUtil.listToCommaSeparated(columnNames)
				+ " from " + fromColumn.getTable().getName();
			
			log.info("Reading data using query: " + sql);
			
			String updateSql = "update " + fromColumn.getTable().getName() 
				+ " set " 
				+ toColumn.getName() + " = ? "
				+ "where ";
			String[] pkNames = new String[pks.length];
			for (int i=0; i<pks.length; i++) {
				pkNames[i] = pks[i].getName();
				if (i > 0) {
					updateSql += ", ";
				}
				updateSql += pkNames[i] + " = ?";
			}
			log.info("Updating rows using: " + updateSql);

			updateStatement = conn.prepareStatement(updateSql);
			rs = stmt.executeQuery(sql);
			while (rs.next()) {
				count++;
				boolean doUpdate = typeConversion.convertType(rs, fromColumn.getName(), updateStatement, 1, fromColumn, toColumn);
				
				if (doUpdate){
					// Set primary keys
					for (int i=0; i<pks.length; i++) {
						String pkName = pks[i].getName();
						Object pkValue = rs.getObject(pkName);
						if (pkValue != null){
							updateStatement.setObject(i + 2, pkValue);
						} else {
							updateStatement.setNull(i + 2, pks[i].getType().getJDBCType());
						}
					}

					count += updateStatement.executeUpdate();

				}
			}
			log.info("Number of rows updated : " + count);
		} finally {
			if (rs != null) {
				rs.close();
			}
			if (stmt != null) {
				stmt.close();
			}
			if (updateStatement != null) {
				updateStatement.close();
			}									
		}
	}
	
	public int getPriority() {
		return SchemaFix.PRIORITY_COLUMN_COPY;
	}

	public String getDescription() {
		return "Copying data to table " + toColumn.getTable() + " column " + toColumn.getName();
	}

	public int getAction() {
		return ACTION_COPY_DATA;
	}

}
